package com.jason.www.service;

import com.jason.www.code.dao.PageEntity;
import com.jason.www.code.redis.BaseRedis;
import com.jason.www.dao.GiftCardMapper;
import com.jason.www.pojo.GiftCard;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.util.ArrayList;
import java.util.List;

import static java.util.Objects.isNull;
import static java.util.Objects.nonNull;
import static java.util.Optional.ofNullable;

/**
 * 关于数据库表主键ID 不通过mysql autoincrment ，而是通过redis来维护  getGiftCardId()
 * SEQ:POJO类名                  用来存储当前最大主键值，用一个String保存起来       set  SEQ:GiftCard  1000001
 * SET:POJO类名                  用来存储所有数据的key值，用一个SET集合保存起来     sadd  SET:GiftCard  GiftCard:1000001
 * POJO类名:主键ID               用来存储一个对象数据，用一个String保存起来         set  GiftCard:1000001  序列化的具体对象
 * SET:POJO类名:IDX              用来存储所有索引的key值，用一个SET集合保存起来     sadd  SET:GiftCard:IDX  SET:GiftCard:CARDNO:00000001
 * SET:POJO类名:大写属性:属性值  用来存储一个具体的索引，用一个SET集合保存起来      sadd  SET:GiftCard:CARDNO:00000001  GiftCard:1000001
 */
@Repository("giftCardRedisImpl")
public class GiftCardRedisImpl extends BaseRedis<String,GiftCard> implements IGiftCardRedis{
    @Autowired
    private GiftCardMapper giftCardDao;

    private final static Long   START_ID = 1000000L;
    private final static String PRE_KEY = GiftCard.class.getSimpleName();
    private final static String SEQ_KEY = new StringBuffer("SEQ:").append(PRE_KEY).toString();
    private final static String SET_KEY = new StringBuffer("SET:").append(PRE_KEY).toString();
    private final static String SET_IDX_KEY = new StringBuffer("SET:").append(PRE_KEY).append(":IDX").toString();

    private String getGiftCardKey(Long id) {
        return new StringBuilder(PRE_KEY).append(":").append(id).toString();
    }
    private String getGiftCardUnqIdx4CardNoSetKey(String cardNo){
        return new StringBuffer(SET_KEY).append(":CardNo:".toUpperCase()).append(cardNo).toString() ;
    }

    @Override
    public Long getGiftCardId() {
        Long id = Long.valueOf(incr(SEQ_KEY).toString());
        // 如果缓存里面还没有，此时id=1,就会到数据库查找
        //如果缓存里面有值的话，就会累加+1返回
        if(id <= 1L){
            id = giftCardDao.selectMaxId();
            if( id == null || id == 0 ){
                id = START_ID ;
            }else{
                id ++;
            }
            set(SEQ_KEY, id.toString(), 0L, true);
        }
        return id ;
    }

    @Override
    public GiftCard addGiftCard(GiftCard giftCard) {
        return addGiftCard(giftCard,0L);
    }

    @Override
    public GiftCard addGiftCard(GiftCard giftCard, long expire) {
        if(giftCard.getId() == null || giftCard.getId() == 0L) {
            giftCard.setId(getGiftCardId());
        }
        giftCardDao.addGiftCard(giftCard);
        setGiftCard2Redis(giftCard);
        return giftCard;
    }

    @Override
    public List<GiftCard> addGiftCardList(List<GiftCard> giftCardList) {
        return addGiftCardList(giftCardList,0L);
    }

    @Override
    public List<GiftCard> addGiftCardList(List<GiftCard> giftCardList, long expire) {
        for (GiftCard giftCard : giftCardList) {
            if(giftCard.getId() == null || giftCard.getId() == 0L) {
                giftCard.setId(getGiftCardId());
            }
            setGiftCard2Redis(giftCard,expire);
        }
        giftCardDao.addGiftCardList(giftCardList);
        return giftCardList;
    }

    @Override
    public void deleteGiftCard(Long id) {
        delGiftCard4Redis(id);
        giftCardDao.deleteGiftCardById(id);
    }

    @Override
    public void deleteGiftCardList(List<GiftCard> giftCardList) {
        List<Long> ids = new ArrayList<>();
        ofNullable(giftCardList).orElse(new ArrayList<>()).forEach(e ->{
            delGiftCard4Redis(e.getId());
            ids.add(e.getId());
        });
        giftCardDao.deleteGiftCardByIds(ids);
    }

    @Override
    public int updateGiftCard(GiftCard giftCard) {
        return updateGiftCard(giftCard,true,0L);
    }

    @Override
    public int updateGiftCard(GiftCard giftCard, boolean syncDB) {
        return updateGiftCard(giftCard,syncDB,0L);
    }

    @Override
    public int updateGiftCard(GiftCard giftCard, boolean syncDB, long expire) {
        String primaryKey =  getGiftCardKey(giftCard.getId());
        GiftCard oldGiftCard = get(primaryKey, GiftCard.class) ;
        // 尝试数据库中再查询一遍
        if ( isNull(oldGiftCard)){
            oldGiftCard = giftCardDao.selectGiftCardByKey(oldGiftCard.getId()) ;
        }
        if( isNull(oldGiftCard)){
            return 0;
        }
        copyPropertiesIgnoreNull(giftCard, oldGiftCard);
        setGiftCard2Redis(oldGiftCard,expire);
        if (syncDB) {
            return giftCardDao.updateGiftCard(oldGiftCard);
        }
        return 1;
    }

    @Override
    public int updateGiftCardList(List<GiftCard> giftCardList) {
        return updateGiftCardList(giftCardList ,true,0L);
    }

    @Override
    public int updateGiftCardList(List<GiftCard> giftCardList, boolean syncDB) {
        return updateGiftCardList(giftCardList ,syncDB,0L);
    }

    @Override
    public int updateGiftCardList(List<GiftCard> giftCardList, boolean syncDB, long expire) {
        ofNullable(giftCardList).orElse(new ArrayList<>()).forEach(giftCard ->{
            String primaryKey =  getGiftCardKey(giftCard.getId());
            GiftCard oldGiftCard = get(primaryKey, GiftCard.class) ;
            // 尝试数据库中再查询一遍
            if (isNull(oldGiftCard)){
                oldGiftCard = giftCardDao.selectGiftCardByKey(oldGiftCard.getId()) ;
            }
            if(isNull(oldGiftCard)){
                giftCardList.remove(giftCard);
            }else{
                copyPropertiesIgnoreNull(giftCard, oldGiftCard);
                setGiftCard2Redis(oldGiftCard,expire);
            }
        });
        if(syncDB && !giftCardList.isEmpty()){
            return giftCardDao.updateGiftCardList(giftCardList);
        }
        return giftCardList.isEmpty()? 0 : 1;
    }

    @Override
    public Long getTotalGiftCard(GiftCard giftCard) {
        String hashKey = getAttrVal4ForRedisKey(giftCard, false);
        Long total = hget(hashKey, PageEntity.TOTAL,Long.class);
        if(isNull(total) || total == 0L){
            total = giftCardDao.getTotalGiftCard(giftCard);
            hset(hashKey,PageEntity.TOTAL,total,PageEntity.EXPIRE);
        }
        return total;
    }

    @Override
    public void loadAllGiftCard() {
        loadAllGiftCard(0L);
    }


    @Override
    public void loadAllGiftCard(long expire) {
        clearAllGiftCard() ;

        GiftCard giftCard = new GiftCard() ;
        List<GiftCard> giftCardList = new ArrayList<>() ;
        Long total = getTotalGiftCard(giftCard);
        Integer totalPage = (int)Math.ceil((double)total / LOAD_PAGE_SIZE);
        giftCard.setPageSize(LOAD_PAGE_SIZE);

        for(int i = 1 ; i <= totalPage; i++){
            giftCard.initPageAndPageSize();
            giftCardList = giftCardDao.selectGiftCardByPage(giftCard);
            for (GiftCard temp : giftCardList) {
                setGiftCard2Redis(temp,expire);
            }
        }
    }

    @Override
    public GiftCard getGiftCardByKey(Long id) {
        return getGiftCardByKey(id,0L);
    }

    @Override
    public GiftCard getGiftCardByKey(Long id, long expire) {
        String giftCardKey = getGiftCardKey(id);
        GiftCard giftCard = get(giftCardKey, GiftCard.class);
        if(isNull(giftCard)){
            giftCard = giftCardDao.selectGiftCardByKey(id);
            if(nonNull(giftCard)){
                setGiftCard2Redis(giftCard,expire);
            }
        }
        return giftCard;
    }

    @Override
    public List<GiftCard> getGiftCardByKeys(List<Long> ids) {
        List<String> keys = new ArrayList<>();
        if(isNull(ids) || ids.isEmpty()){
            return null;
        }

        ids.stream().forEach(id -> keys.add(getGiftCardKey(id)));
        List<GiftCard> result = mget(keys, GiftCard.class);
        if(result.size() == ids.size()){ return result;}

        result = giftCardDao.selectGiftCardByKeys(ids);
        ofNullable(result).orElse(new ArrayList<>()).forEach(this::setGiftCard2Redis);

        return result;
    }

    @Override
    public List<GiftCard> getGiftCardByPage(GiftCard giftCard, Boolean fromCache) {
        return getGiftCardByPage(giftCard, fromCache,0L);
    }

    @Override
    public List<GiftCard> getGiftCardByPage(GiftCard giftCard, Boolean fromCache, long expire) {
        ArrayList<GiftCard> result = new ArrayList<>();
        String hashKey = getAttrVal4ForRedisKey(giftCard, false);
        giftCard.initPageAndPageSize();
        if(fromCache) {
            result = hget(hashKey, giftCard.getPage().toString(), ArrayList.class) ;
        }
        if(isNull(result) || result.isEmpty()){
            result = (ArrayList<GiftCard>) giftCardDao.selectGiftCardByPage(giftCard);
            hset(hashKey, giftCard.getPage().toString(), result, expire) ;
        }
        return result;
    }

    private void setGiftCard2Redis(GiftCard giftCard){
        setGiftCard2Redis(giftCard,0L);
    }
    private void setGiftCard2Redis(GiftCard giftCard, long expire){
        String giftCardKey = getGiftCardKey(giftCard.getId());
        sadd(SET_KEY, giftCardKey);
        set(giftCardKey, giftCard, expire);

        // 处理唯一索引情况
        if(nonNull(giftCard) && nonNull(giftCard.getCardNo())){
            String uniqueIdxKey = getGiftCardUnqIdx4CardNoSetKey(giftCard.getCardNo());
            sadd(SET_IDX_KEY, uniqueIdxKey) ;
            sadd(uniqueIdxKey,giftCardKey) ;
        }
    }

    private void delGiftCard4Redis(Long id) {
        String giftCardKey = getGiftCardKey(id);
        srem(SET_KEY,giftCardKey);

        GiftCard giftCard = getGiftCardByKey(id);
        delete(giftCardKey);

        // 处理唯一索引情况
        if(nonNull(giftCard)){
            String uniqueIndexSetKey = getGiftCardUnqIdx4CardNoSetKey(giftCard.getCardNo());
            srem(SET_IDX_KEY, uniqueIndexSetKey);
            srem(uniqueIndexSetKey,giftCardKey);
        }
    }

    /**
     * 清空所有数据
     * 根据keySet的key     获取所有存储的所有对象对应的KEY
     * 根据idxKeySET的key 获取所有存储的所有索引对应的KEY
     * 删除 keySet的key ，idxKeySET的key ，所有对象对应的KEY ，所有索引对应的KEY
     */
    private void clearAllGiftCard() {
        @SuppressWarnings("unchecked")
        List<String> listKey = (List<String>)smembers(SET_KEY);
        @SuppressWarnings("unchecked")
        List<String> listIdxKey = (List<String>)smembers(SET_IDX_KEY);
        listKey.addAll(listIdxKey);

        listKey.add(SET_KEY);
        listKey.add(SET_IDX_KEY);

        if(listKey.size() > 0){
            delete(listKey) ;
        }
    }
}
